#ifndef __TEXTURE_H__
#define __TEXTURE_H__

#pragma once

#include "HardwareBuffer.h"

namespace Nezha
{
	// texture format
	enum TexFormat
	{
		TF_NONE,

		// common
		TF_R5G6B5,
		TF_B5G6R5,
		TF_A1R5G5B5,
		TF_A1B5G5A5,
		TF_A4R4G4B4,
		TF_A8,
		TF_R8G8B8,
		TF_B8G8R8,
		TF_A8R8G8B8,
		TF_A8B8G8R8,
		TF_A2R10G10B10,
		TF_A2B10G10R10,

		// luminance, 8-bit
		TF_L8,
		TF_L16,
		TF_A4L4,
		TF_A8L8,

		// 16-bit short
		TF_GR16,
		TF_RGB16,
		TF_RGBA16,

		// 16-bit floating-point formats
		TF_R16F,
		TF_GR16F,
		TF_RGB16F,
		TF_RGBA16F,

		// 32-bit floating-point formats
		TF_R32F,
		TF_GR32F,
		TF_RGB32F,
		TF_RGBA32F,

		// DXT compressed formats.
		TF_DXT1,
		TF_DXT2,
		TF_DXT3,
		TF_DXT4,
		TF_DXT5,

		// Depth-stencil format.
		TF_D24S8,

		// PVR
		TF_PVR_RGB2,
		TF_PVR_RGBA2,
		TF_PVR_RGB4,
		TF_PVR_RGBA4,

		TF_NUM
	};

	// texture type
	enum TexType
	{
		TT_1D,
		TT_2D,
		TT_3D,
		TT_CUDE,
		TT_ARRAY,

		TT_NUM
	};

	class NEZHA_ENGINE_ENTRY _Image2D : public Refable
	{
	private:
		u8* mBuffer;
		u32 mSizeInBytes;
	public:
		TexFormat mFormat;
		u16 mWidth;
		u16 mHeight;

		const u8* getData() const
		{
			return mBuffer;
		}

		u8* getData()
		{
			return mBuffer;
		}

		u32 getSize() const
		{
			return mSizeInBytes;
		}

		void allocate(u32 sizeInBytes);

		_Image2D()
			:mFormat(TF_NONE)
			,mBuffer(NULL)
			,mSizeInBytes(0)
			,mWidth(0)
			,mHeight(0)
		{
		}

		~_Image2D();
	};

	typedef RefCountedPtr<_Image2D>::Default _Image2DPtr;


	/** basic class fo texure. */
	class NEZHA_ENGINE_ENTRY Texture : public HardwareBuffer
	{
		friend class TextureManager;
	public:
		virtual ~Texture(){}

		TexFormat getFormat() const;

		virtual TexType getType() const
		{
			return TT_2D;
		}

		bool isCompressed() const;
		bool hasMipmaps() const;
		u16 getWidth() const;
		u16 getHeight() const;
		u16 getDepth() const;

		int getID() const;

NEZHA_INTERNAL:
		//virtual void _generateMipmaps() = 0;

	protected:
		Texture(HardwareBuffer::Usage usage, TexFormat tf, u16 w, u16 h, u16 depth, bool createShadowBuffer,
			bool hasMipmap, bool isCompressed)
			:HardwareBuffer(usage, createShadowBuffer)
			,mFormat(tf)
			,mWidth(w)
			,mHeight(h)
			,mDepth(depth)
			,mHasMipmaps(hasMipmap)
			,mCompressed(isCompressed)
		{
		}

	private:
		void _setID(int id)
		{
			mID = id;
		}

		int mID;

	protected:
		TexFormat mFormat;

		u16 mWidth;
		u16 mHeight;
		u16 mDepth;

		bool mHasMipmaps;
		bool mCompressed;
	};

	typedef RefCountedPtr<Texture>::Default TexturePtr;

	class IReadFile;

	// FIXME texture usage.
	class NEZHA_ENGINE_ENTRY TextureManager : public RefCountedResManager
	{
	public:
		virtual ~TextureManager();

		TexturePtr create2DTexture(const _Image2D& img2d, bool mipmap = true, HardwareBuffer::Usage usage = HardwareBuffer::HBU_STATIC);

		TexturePtr getTexture(u32 id);

		void removeAll();

		_Image2DPtr load2DImage(const _string& filePath);

		_Image2DPtr loadPNG(const _string& filePath);

		_Image2DPtr loadCompressedPVR(const _string& filePath);

		static u16 TexFormatBits(TexFormat fmt);

		typedef PMap<u32, TexturePtr> TextureIDMap;
		typedef PMap<_string, Texture*, _NzStrLess> TextureNameMap; 

	protected:
		TextureManager();

		virtual TexturePtr _create2DTexture(const _Image2D& img2d, bool mipmap, HardwareBuffer::Usage usage) = 0;

		void insertTexture(const TexturePtr& tex);

		virtual void releaseTextureImpl(const TexturePtr& tex) = 0;

		void _garbageDispose();

		int getUnusedTextureID()
		{
			int id = mMaxUnusedID;
			++mMaxUnusedID;
			return id;
		}

		_Image2D* _loadPVR(IReadFile* file);
	private:
		TextureIDMap mTextures;

		int mMaxUnusedID;
	};

	inline TexFormat Texture::getFormat() const
	{
		return mFormat;
	}

	inline bool Texture::isCompressed() const
	{
		return mCompressed;
	}

	inline bool Texture::hasMipmaps() const
	{
		return mHasMipmaps;
	}

	inline u16 Texture::getWidth() const
	{
		return mWidth;
	}

	inline u16 Texture::getHeight() const
	{
		return mHeight;
	}

	inline u16 Texture::getDepth() const
	{
		return mDepth;
	}

	inline int Texture::getID() const
	{
		return mID;
	}

}//end namespace Nezha

#endif //end __TEXTURE_H__